外观
7. Dockerfile 基础概念
Dockerfile 是用于构建 Docker 镜像的文本文件,包含了一系列构建指令和参数。通过 Dockerfile,可以自动化构建镜像,确保环境一致性和可重复性。
Dockerfile 命名规范
Dockerfile 文件名通常直接命名为 Dockerfile,也可以使用特定命名如 Dockerfile.prod。构建时通过 -f 参数指定文件路径。
7.1 Dockerfile 构建流程
7.2 核心指令详解
7.2.1 FROM - 指定基础镜像
FROM 指令用于指定构建镜像的基础镜像,是 Dockerfile 的第一条指令(注释除外)。
dockerfile
# 使用官方镜像
FROM ubuntu:20.04
# 使用 Alpine Linux(镜像更小)
FROM alpine:3.18
# 使用多阶段构建的基础镜像
FROM golang:1.21-alpine AS builder1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
基础镜像选择
优先选择官方镜像,使用 Alpine 版本可显著减小镜像体积。生产环境建议使用固定版本标签而非 latest。
7.2.2 RUN - 执行构建命令
RUN 指令在镜像构建过程中执行命令,每条 RUN 指令会在当前镜像基础上创建一个新层。
dockerfile
# 安装系统依赖
RUN apt-get update && apt-get install -y \
curl \
wget \
git \
&& rm -rf /var/lib/apt/lists/*
# 编译应用程序
RUN cd /app && \
npm install && \
npm run build1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
RUN 指令优化
将多个命令用 && 连接,减少镜像层数。最后清理缓存文件减小镜像体积。
7.2.3 COPY 与 ADD - 文件复制
COPY 指令
COPY 指令将文件从宿主机复制到镜像中,是最常用的文件复制方式。
dockerfile
# 复制源代码
COPY . /app
# 复制特定文件
COPY package.json package-lock.json /app/
# 复制目录
COPY src/ /app/src/1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
ADD 指令
ADD 指令功能类似 COPY,但支持更多特性:
dockerfile
# 复制并自动解压 tar 文件
ADD app.tar.gz /app/
# 从 URL 下载文件
ADD https://example.com/file.zip /tmp/1
2
3
4
5
2
3
4
5
ADD vs COPY
优先使用 COPY,除非需要自动解压或从 URL 下载文件。ADD 可能引入安全风险。
7.2.4 WORKDIR - 设置工作目录
WORKDIR 指令设置后续指令的工作目录,类似于 cd 命令。
dockerfile
# 设置工作目录
WORKDIR /app
# 后续指令都在 /app 目录下执行
COPY . .
RUN npm install1
2
3
4
5
6
2
3
4
5
6
工作目录建议
使用绝对路径,避免相对路径可能造成的混乱。常用 /app 作为应用根目录。
7.2.5 ENV - 设置环境变量
ENV 指令设置环境变量,可在构建时和运行时使用。
dockerfile
# 设置环境变量
ENV NODE_ENV=production
ENV PORT=3000
# 在 RUN 指令中使用
RUN echo "Building for $NODE_ENV environment"1
2
3
4
5
6
2
3
4
5
6
7.2.6 EXPOSE - 声明端口
EXPOSE 指令声明容器运行时监听的端口,但不会自动映射到宿主机。
dockerfile
# 声明单个端口
EXPOSE 3000
# 声明多个端口
EXPOSE 3000 80 443
# 声明 TCP/UDP 端口
EXPOSE 53/udp 53/tcp1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
端口映射
EXPOSE 只是声明,实际映射需要 docker run -p 或 docker-compose 配置。
7.2.7 VOLUME - 定义挂载点
VOLUME 指令创建挂载点,用于持久化数据或共享数据。
dockerfile
# 定义数据卷
VOLUME ["/app/data", "/app/logs"]
# 或者使用 JSON 数组格式
VOLUME ["/tmp"]1
2
3
4
5
2
3
4
5
数据持久化
VOLUME 定义的目录会被 Docker 管理,容器删除后数据仍然存在。
7.3 CMD 与 ENTRYPOINT
7.3.1 CMD - 默认启动命令
CMD 指令指定容器启动时默认执行的命令,可以被 docker run 的参数覆盖。
dockerfile
# shell 格式
CMD npm start
# exec 格式(推荐)
CMD ["npm", "start"]
# 作为参数传递
CMD ["nginx", "-g", "daemon off;"]1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
7.3.2 ENTRYPOINT - 入口点
ENTRYPOINT 指令设置容器启动时的主命令,不能被 docker run 参数覆盖,但可以追加参数。
dockerfile
# 设置入口点
ENTRYPOINT ["nginx"]
# 或者使用 shell 格式
ENTRYPOINT nginx -g "daemon off;"1
2
3
4
5
2
3
4
5
7.3.3 CMD 与 ENTRYPOINT 对比
| 特性 | CMD | ENTRYPOINT |
|---|---|---|
| 可覆盖性 | 可被 docker run 参数完全覆盖 | 只能追加参数到 ENTRYPOINT 后 |
| 用途 | 默认命令,灵活性高 | 固定主命令,参数可变 |
| 格式 | CMD ["cmd"] 或 CMD cmd | ENTRYPOINT ["cmd"] 或 ENTRYPOINT cmd |
dockerfile
# CMD 示例:可完全覆盖
# docker run myapp npm test # 覆盖默认的 npm start
CMD ["npm", "start"]
# ENTRYPOINT 示例:只能追加参数
# docker run myapp -g "daemon off;" # 追加到 nginx 后
ENTRYPOINT ["nginx"]1
2
3
4
5
6
7
2
3
4
5
6
7
7.4 高级应用场景
7.4.1 多阶段构建
多阶段构建可以显著减小最终镜像体积,特别适合编译型语言。
dockerfile
# 构建阶段
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o main .
# 运行阶段
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/main .
EXPOSE 8080
CMD ["./main"]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
多阶段构建优势
构建工具和依赖不会包含在最终镜像中,显著减小镜像体积。
7.4.2 常用 Dockerfile 模板
Node.js 应用
dockerfile
FROM node:18-alpine
# 设置工作目录
WORKDIR /app
# 复制依赖文件
COPY package*.json ./
# 安装依赖
RUN npm ci --only=production
# 复制源代码
COPY . .
# 创建非 root 用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
USER nextjs
# 暴露端口
EXPOSE 3000
# 启动命令
CMD ["npm", "start"]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Python 应用
dockerfile
FROM python:3.11-slim
# 设置工作目录
WORKDIR /app
# 设置环境变量
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
# 复制依赖文件
COPY requirements.txt .
# 安装依赖
RUN pip install --no-cache-dir -r requirements.txt
# 复制源代码
COPY . .
# 创建非 root 用户
RUN useradd --create-home --shell /bin/bash app \
&& chown -R app:app /app
USER app
# 暴露端口
EXPOSE 8000
# 启动命令
CMD ["python", "app.py"]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
7.5 最佳实践
安全考虑
- 使用非 root 用户运行应用
- 定期更新基础镜像修复安全漏洞
- 不要在镜像中存储敏感信息
性能优化
- 使用多阶段构建减小镜像体积
- 将变更频率低的指令放在前面
- 合理使用缓存层
常见错误
- 忘记清理包管理器缓存导致镜像过大
- 使用 root 用户运行应用存在安全风险
- 在同一层执行多个不相关操作影响缓存效率
7.6 构建与使用
7.6.1 构建镜像
bash
# 基础构建
docker build -t myapp:1.0 .
# 使用特定 Dockerfile
docker build -f Dockerfile.prod -t myapp:prod .
# 显示构建过程
docker build --progress=plain -t myapp:1.0 .1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
7.6.2 查看构建历史
bash
# 查看镜像层信息
docker history myapp:1.0
# 检查镜像大小
docker images myapp1
2
3
4
5
2
3
4
5
7.6.3 故障排查
bash
# 查看构建缓存
docker builder prune -f
# 清理未使用的构建缓存
docker system prune -f1
2
3
4
5
2
3
4
5